﻿using System;
using System.Collections.Generic;

using System.Text;
using WINX32Lib;
using System.Threading;
using System.Runtime.InteropServices;
using Ivi.Driver.Interop;

namespace AIRO.Devices.Ccd
{
    [Guid("886B9B87-5292-411a-899D-3589C96918DF")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("AIRO.Devices.Ccd.PrinstonCCD")]
    public class PrinstonCCD : BasicIviDriver, ICcd, IIviDriver, IIviDriverIdentity, IIviComponentIdentity, IIviDriverOperation, IIviDriverUtility
    {
        private IExpSetup wsExp;
        private int xDim;
        private int yDim;
        private IDocFile wsDoc;
        private SynchronizationContext context;
        private CcdState _state;
        private delegate bool MeasureDelegate();
        private MeasureDelegate del;
        private AsyncCallback measurementCompleteCallback;
        private object frame;
        

        public PrinstonCCD()
            : base()
        {
            _description = "IVI Custom Driver for Prinston CCD";
            _supportedInstrumentModels = "Prinston CCD";
            _identifier = "Prinston CCD";
            _vendor = "LNEP ISSP(RAS)";
            _instrumentManufacturer = "Prinston";
            context = SynchronizationContext.Current;
            measurementCompleteCallback = delegate(IAsyncResult ar)
            {
                bool res = del.EndInvoke(ar);
                if (res)
                {
                    OnMeasurementCompleteEvent(new EventArgs());
                }
                else
                {
                    OnStateChangedEvent(new StateChangedEventArgs(CcdState.Error));
                }


            };
            del = this.Measure;
        }
        

        public void Initialize(string ResourceName, bool IdQuery, bool Reset, string OptionString)
        {

            if (!_initialized)
            {
                _rangeCheck = true;
                _cache = true;
                _queryInstrumentStatus = false;
                _simulate = false;
                _driverSetup = "";
                _recordCoercions = false;
                _interchangeCheck = false;
                resourceName = "Not Applicable";
                _logicalName = ResourceName;
                                                
                if (Reset)
                {
                    this.Reset();
                }
                wsExp = new ExpSetupClass();

                object xuint = dataType.X_LONG;
                wsExp.SetParam(EXP_CMD.EXP_DATATYPE, ref xuint);


                _initialized = true;

            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_ALREADY_INITIALIZED));
            }
        }

        public void Close()
        {
            if (_initialized)
            {
            _initialized = false;
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        public void Disable()
        {
            if (_initialized)
            {

            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
            }
        }


        #region ICcd Members

        public CcdState State
        {
            get { return _state; }
        }

        public int NumberOfSpectra
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void StartMeasure()
        {
            del.BeginInvoke(measurementCompleteCallback, null);
        }

        public bool Measure()
        {
            bool res = wsExp.Start(ref wsDoc);
            if (!res)
            {
                //worker.ReportProgress(0, new WSState(-1,"WinSpec can't start experiment"));
                //frm.OnWSError(this,new ErrorEventArgs("WinSpec can't start experiment"));
                _state = CcdState.Error;
                OnStateChangedEvent(new StateChangedEventArgs(_state, "WinSpec can't start experiment"));
                return false;
            }                       
            wsExp.WaitForExperiment();
            return true;
            
        }

        public Array GetFrame(short frameNumber)
        {
            if (wsDoc != null)
            {
                
                wsDoc.GetFrame(frameNumber, ref frame);
                return (Array)frame;
                
            }
            else
            {
                throw new Exception("Call Measure before 'GetFrame'");
            }
        }

        public int Rank
        {
            get { return 2; }
        }

        public int GetDimension(int indx)
        {
            if (wsDoc != null)
            {
                short pbRes;
                if (indx == 0)
                {
                    if (xDim == 0)
                        xDim = (int)wsDoc.GetParam(DM_CMD.DM_XDIM, out pbRes);
                    return xDim;
                }
                else if (indx == 1)
                {
                    if (yDim == 0)
                        yDim = (int)wsDoc.GetParam(DM_CMD.DM_YDIM, out pbRes);
                    return yDim;
                }
                else
                {
                    throw new IndexOutOfRangeException("indx argument should be 0 or 1");
                }
            }
            else
            {
                throw new Exception("Call 'Measure' before calling 'GetDimension'");
            }

            
        }

        public bool IsExpCompleted()
        {
            bool running;
            short pRes;
            running = (bool)wsExp.GetParam(EXP_CMD.EXP_RUNNING, out pRes);
            return !running;
        }

        protected virtual void OnStateChangedEvent(StateChangedEventArgs e)
        {
            context.Post(new SendOrPostCallback(delegate(object obj)
            {
                StateChangedEventArgs args = (StateChangedEventArgs)obj;
                EventHandler<StateChangedEventArgs> handler = StateChangedEvent;
                if (handler != null)
                {
                    handler(this, e);
                }
            }), e);
        }

        protected virtual void OnMeasurementCompleteEvent(EventArgs e)
        {
            context.Post(new SendOrPostCallback(delegate(object obj)
            {
                EventArgs args = (EventArgs)obj;
                EventHandler<EventArgs> handler = MeasurementCompleteEvent;
                if (handler != null)
                {
                    handler(this, args);
                }
            }), e);
        }



        public event EventHandler<StateChangedEventArgs> StateChangedEvent;

        public event EventHandler<EventArgs> MeasurementCompleteEvent;

        #endregion
    }
}
